﻿#include "lix/lix.h"
#include "lix/text.h"
#include "lix/xml/document.h"
#include "lix/xml/parser.h"


namespace lix{	
	namespace xml{


//==============================================================================
// lix::xml::NodeAllocator
//==============================================================================

// コンストラクタ
NodeAllocator::NodeAllocator()
{
	m_AllocationCache = NULL;
}

// デストラクタ
NodeAllocator::~NodeAllocator()
{
}

// 生成
bool NodeAllocator::create()
{
	return m_Body.create();
}

// ノード生成
Node* NodeAllocator::allocateNode( NodeType type )
{
	void* memory = allocateItem();
	if( ! memory ){
		return NULL;
	}
		

	
	Node* r = new (memory) Node( type );
	return r;
}

void NodeAllocator::deallocateNode( Node* node )
{
	if( node == NULL ){
		return;
	}
	
	// デストラクタを呼ぶ
	node ->~Node();
	
	deallocateItem( node );

	
}

// テキストノードを作る
Node* NodeAllocator::createTextNode( const CharSequence& text )
{
	Node* node = allocateNode( xml::kNodeText );
	node->m_Value = text;
	return node;
}

// doctype 作る
Node* NodeAllocator::createDocType( const CharSequence& name, const CharSequence& value )
{
	Node* node = allocateNode( xml::kNodeDoctype );
	node->m_Name = name;
	node->m_Value = value;
	return node;
}

// xml宣言 作る
Node* NodeAllocator::createDeclare( const CharSequence& name, const CharSequence& value )
{
	Node* node = allocateNode( xml::kNodeXmlDeclare );
	node->m_Name = name;
	node->m_Value = value;
	return node;
}

Node* NodeAllocator::createCData( const CharSequence& value )
{
	Node* node = allocateNode( xml::kNodeCData );
	node->m_Value = value;
	return node;
}

Node* NodeAllocator::createAttribute( const CharSequence& name, const CharSequence& value )
{
	Node* node = allocateNode( xml::kNodeAttribute );
	node->m_Name = name;
	node->m_Value = value;
	return node;
}

size_t NodeAllocator::getItemSize()
{
	return kNodeSize;
}

void* NodeAllocator::allocateItem()
{
	void* memory = NULL;
			
	if( m_AllocationCache ){
		memory = m_AllocationCache;
		m_AllocationCache = NULL;
	}else{
		memory = m_Body.allocate();
	}
	return memory;
}

void NodeAllocator::deallocateItem( void* p )
{
	// 極稀なケースだが FixedAllocator が 確保 -> 解放 -> 確保 -> 解放 を繰り返した時非常に重くなるパターンがある
	// そのために一個分だけメモリをキャッシュする
	// (同じ状況で２つ確保２つ開放を繰り返せばやっぱりそのパターンにハマるんだが、さすがにそれはないだろう）

	// 一つもキャッシュしていない場合はそれをキャッシュとして解放はしない。
	if( ! m_AllocationCache ){
		m_AllocationCache = p;	
		return;
	}
	
	m_Body.deallocate( p );
}









//==============================================================================
// Parser
//==============================================================================

	
Parser::Parser( Node* root_node, NodeAllocator* allocator )
{
	m_Allocator = allocator;
	m_RootNode = root_node;
	m_ErrorNode = NULL;
	m_ParseOptions = 0;
}

// パース
lix::xml::ParseResult Parser::parse(const char* text, uint32_t options)
{
	LIX_ASSERT( text != NULL );
	if( Text::isEmpty( text )){
		return xml::kParseEmptyText;
	}
	
	LIX_ASSERT( m_RootNode );
	LIX_ASSERT( m_Allocator );
	
	m_ParseOptions = options;

	// BOM 読み飛ばし
	skipBOM( text );

	// ルートノードの中をパース
	m_NodePath.push_back( m_RootNode );

	ParseResult r = parseElementBody( m_RootNode, text );
	return r;
}



// Node内パース
ParseResult Parser::parseElementBody( Node* node, const char*& pch )
{
	while( true ){

		// 次のタグ開始までのテキスト
		const char* next = findStartOfTag( pch );
		CharSequence text_sequence = CharSequence::create( pch, next );
		
		// 閉じタグまできたか？
		bool is_closing = false;

		if( *next == 0 ){
			if( ! node->isRootNode() ){
				m_ErrorNode = node;
				// LIX_LOG_DEBUG( "element %s has no end tag.", node->getName().c_str());
				return kParseNoEndTag;
			}
			is_closing = true;
		}else{
			LIX_ASSERT( *next == '<' );

			// 閉じタグであるか調べる、閉じタグである場合は副作用で next が進む。
			CharSequence close_name;
			is_closing = parseCloseTag( next, close_name );
			
			if( ! node->isRootNode() && ! close_name.isEmpty() ){
				if( close_name != node->m_Name ){
					m_ErrorNode = node;
					// LIX_LOG_DEBUG( "end tag mismatch ", node->getName(), close_name );
					return kParseInvalidEndTag;
				}
			}
		}

		// 閉じタグ
		if( is_closing ){
			
			// 今回のテキストノードは追加すべきか？
			bool to_add_text = true;

			// 空のテキストノード
			if( text_sequence.isEmpty() && ( m_ParseOptions & option::kSkipEmptyTextNode )){
				to_add_text = false;
			}

			// 兄弟がいてホワイトスペースのみ、
			if( to_add_text && node->getFirstChild() != NULL ){
				if( ( m_ParseOptions & option::kSkipMixedWhiteSpace ) && Text::isWhiteSpace( text_sequence ) ){
					to_add_text = false;
				}
			}

			if( to_add_text ){
				Node* text_node = m_Allocator->createTextNode( text_sequence );
				node->addChild( text_node );
			}			
			pch = next;
			return kParseOK;
		}

		// 閉じタグではなく < にある。

		// パース、副作用で next が進む、
		ParseResult r = parseNode( node, next );
		
		if( r != kParseOK ){
			return r;
		}

		pch = next;
	}
	

}

// ノードをパース、pch をノードの直後まですすめる
ParseResult Parser::parseNode( Node* parent, const char*& pch )
{
	LIX_ASSERT( *pch == '<' );
	NodeType type = recognizeNodeType( pch );

	// doctype
	if( type == kNodeDoctype ){
		return parseDoctype( parent, pch );
	}
		
	// declear
	if( type == kNodeXmlDeclare ){
		return parseXmlDeclare( parent, pch );
	}

	if( type == kNodeCData ){
		return parseCData( parent, pch );
	}

	if( type == kNodeComment ){
		return parseComment( parent, pch );
	}

	if( type == kNodeProcInst ){
		return parseProcInst( parent, pch );
	}

	if( type == kNodeElement ){
		return parseElement( parent, pch );
	}

	LIX_ASSERT_NEVER_THRU();
	
	return kParseInvalidTagFormat;
}

// Doctype
ParseResult Parser::parseDoctype(  Node* parent, const char*& pch )
{
	const char* end = findEndOfTag( pch );
	
	if( *end != '>' ){
		// LIX_LOG_DEBUG( "InvalidDocType" );
		return xml::kParseInvalidTagFormat;
	}
	const char* end_of_tag = end + 1; 
	

	// Doctype無視フラグ
	if( m_ParseOptions & option::kSkipDoctype ){
		pch = end + 1;
		return xml::kParseOK;
	}

	const char* start = pch+1;
	
	// <!DOCTYPE で、9文字、これはここに来る前の判定で文字は続いていることは保証されてる。
	pch += 9;

	// <!DOCTYPEと思った？残念！さやかちゃんでした！/>
	if( ! Character::isWhiteSpace( *pch )){
		// LIX_LOG_DEBUG( "InvalidDocType" );
		return xml::kParseInvalidTagFormat;
	}

	CharSequence name = CharSequence::create( start, pch );
	skipWhiteSpace( pch );
	
	if( *(end-1) == '/' ){
		end -= 1;
	}

	CharSequence value = CharSequence::create( pch, end ).trim();

	Node* node = m_Allocator->createDocType( name, value );
	parent->addChild( node );

	pch = end_of_tag;
	return xml::kParseOK;

}

// xml 宣言　<?xml  ... ?>
lix::xml::ParseResult Parser::parseXmlDeclare( Node* parent, const char*& pch )
{
	const char* end = findEndOfTag( pch );
	if( *end != '>' ){
		// LIX_LOG_DEBUG( "Invalid xml declare" );
		return xml::kParseInvalidTagFormat;
	}
	const char* end_of_tag = end + 1; 
	

	// 無視フラグ
	if( m_ParseOptions & option::kSkipXmlDeclare ){
		pch = end + 1;
		return xml::kParseOK;
	}

	const char* start = pch+1;
	LIX_ASSERT( *start == '?' );

	// <?xml
	pch += 5;
	CharSequence name = CharSequence::create( start, pch );
	
	skipWhiteSpace( pch );
	
	if( *(end-1) == '?' ){
		end -= 1;
	}

	CharSequence value = CharSequence::create( pch, end ).trim();

	Node* node = m_Allocator->createDeclare( name, value );
	parent->addChild( node );

	pch = end_of_tag;
	return xml::kParseOK;
}

// <![CDATA[  ... ]]>		name はナシ、value は内容
lix::xml::ParseResult Parser::parseCData( Node* parent, const char*& pch )
{
	// <![CDATA[
	pch += 9;

	const char* end = strstr( pch, "]]>" );
	if( end == NULL ){
		// LIX_LOG_DEBUG( "Invalid CDATA" );
		return xml::kParseInvalidTagFormat;
	}
	
	const char* end_of_tag = end + 3;

	CharSequence value = CharSequence::create( pch, end );
	
	Node* node = m_Allocator->createCData( value );
	parent->addChild( node );

	pch = end_of_tag;
	return xml::kParseOK;
}

// <!--  -->				name value ともナシ
lix::xml::ParseResult Parser::parseComment( Node* parent, const char*& pch )
{
	// 厳密にはこれじゃダメらしいけど
	const char* end = strstr( pch, "-->" );
	if( end == NULL ){
		// LIX_LOG_DEBUG( "Invalid COMMENT" );
		return xml::kParseInvalidTagFormat;
	}
	pch = end + 3;

	// 無視フラグ
	if( m_ParseOptions & option::kSkipComment ){
		return xml::kParseOK;
	}

	Node* node = m_Allocator->allocateNode( kNodeComment );
	parent->addChild( node );
	return xml::kParseOK;
}

// <?  ?>					name はナシ、value は内容全部
lix::xml::ParseResult Parser::parseProcInst( Node* parent, const char*& pch )
{
	const char* end = findEndOfTag( pch );
	if( *end != '>' ){
		//LIX_LOG_DEBUG( "Invalid process intruction tag." );
		return xml::kParseInvalidTagFormat;
	}
	const char* end_of_tag = end + 1; 
	
	// 無視フラグ
	if( m_ParseOptions & option::kSkipPI ){
		pch  = end_of_tag;
		return xml::kParseOK;
	}
	
	pch += 2;
	
	if( *(end-1) != '?' ){
		// LIX_LOG_DEBUG( "Invalid process intruction tag." );
		return xml::kParseInvalidTagFormat;
	}
	
	CharSequence value;
	if( pch < end - 1 ){
		value = CharSequence::create( pch, end - 1 );
	}
	

	Node* node = m_Allocator->allocateNode( kNodeProcInst );
	node->m_Value = value;
	parent->addChild( node );
	
	pch  = end_of_tag;
	return xml::kParseOK;
}

// <element></element>	name はタグ名、value はナシ
lix::xml::ParseResult Parser::parseElement( Node* parent, const char*& pch )
{
	// アロケーションなくこれをパースするのは非常に面倒くさい。
	// ・・・でも頑張る
	
	LIX_ASSERT( *pch == '<' );
	++pch;

	// 名前の開始
	skipWhiteSpace( pch );
	if( ! isXmlNameChar( *pch )){
		m_ErrorNode = parent;
		return xml::kParseInvalidTagFormat;
	}
	
	// 名前の範囲
	const char* start_of_name = pch;
	const char* end_of_name = findEndOfXmlName( pch );
	if( *end_of_name == 0 ){
		m_ErrorNode = parent;
		return xml::kParseInvalidTagFormat;
	}

	// ノードを作る
	Node* node = m_Allocator->allocateNode( kNodeElement );
	node->m_Name = CharSequence::create( start_of_name, end_of_name );
	parent->addChild( node );
	
	bool is_empty_element = false;

	// アトリビュートパースループ
	pch = end_of_name;
	while( true ){

		// 空白読み飛ばし
		skipWhiteSpace( pch );
		if( *pch == 0 ){
			m_ErrorNode = node;
			return xml::kParseInvalidTagFormat;
		}

		// 開始タグ終わり
		if( *pch == '>' ){
			break;
		}
		
		// 空エレメント
		if( *pch == '/' ){
			if( is_empty_element ){
				m_ErrorNode = node;
				return xml::kParseInvalidTagFormat;
			}
			is_empty_element = true;
			++pch;
			continue;
		}

		// アトリビュート
		const char* start_of_att_name = pch;
		const char* end_of_att_name = pch = findEndOfXmlName( pch );
		CharSequence att_name = CharSequence::create( start_of_att_name, end_of_att_name );

		skipWhiteSpace( pch );
		
		// 値のないアトリビュート
		if( *pch != '=' ){
			Node* attr = m_Allocator->createAttribute( att_name, att_name );
			node->addAttribute( attr );
			continue;
		}
		
		// アトリビュートの値
		pch += 1;
		CharSequence att_value;
		if( ! parseAttributeValue( pch, att_value )){
			m_ErrorNode = node;
			return xml::kParseInvalidAttribute;
		}

		Node* attr = m_Allocator->createAttribute( att_name, att_value );
		node->addAttribute( attr );
		continue;
	}

	LIX_ASSERT( *pch == '>' );
	pch += 1;

	// 空エレメントならここまで
	if( is_empty_element ){
		return xml::kParseOK;
	}
	
	ParseResult r =  parseElementBody( node, pch );
	if( r != xml::kParseOK ){
		return r;
	}
	return r;
}


bool Parser::parseAttributeValue( const char*& pch, CharSequence& value )
{
	skipWhiteSpace( pch );
	if( *pch == 0 ){
		return false;
	}

	char quote = *pch;
	if( quote != '\'' && quote != '\"' ){
		return false;
	}

	const char* start = pch + 1;
	const char* end = findIf( start, Parser::EqualTo<char>(quote));
	
	if( * end == 0 ){
		return false;
	}

	value = CharSequence::create( start, end );
	pch = end+1;

	return true;
}


// ノード種判別
lix::xml::NodeType Parser::recognizeNodeType( const char* pch )
{
	LIX_ASSERT( *pch == '<' );
	
	if( Text::startsWith( pch, "<!" )){

		if( Text::startsWith( pch, "<!DOCTYPE" )){
			return xml::kNodeDoctype;
		}
		if( Text::startsWith( pch, "<!--" )){
			return xml::kNodeComment;
		}
		if( Text::startsWith( pch, "<![CDATA[" )){
			return xml::kNodeCData;
		}
		return xml::kNodeInvalid;
	}

	if( Text::startsWith( pch, "<?" )){
		if( Text::startsWith( pch, "<?xml" )){
			return xml::kNodeXmlDeclare;
		}
		return xml::kNodeProcInst;
	}

	return xml::kNodeElement;
}



// 閉じタグ解釈
bool Parser::parseCloseTag( const char*& pch, CharSequence& name )
{
	LIX_ASSERT( *pch == '<' );
	
	const char* p = pch + 1;
	skipWhiteSpace( p );

	if( *p != '/' ){
		return false;
	}
	
	p += 1;
	skipWhiteSpace( p );

	// 名無し終了タグ ( "</>" )
	if( *p == '>' ){
		name.clear();
		pch = p+1;
		return true;
	}
	
	// いま p は閉じタグの名前の最初にある
	const char* start_of_name = p;
	const char* end_of_name = findEndOfXmlName( p );

	p = end_of_name;
	
	p = findOneOf( p, "><");
	
	// 閉じダグが閉じてない！
	if( *p != '>' ){
		return false;
	}
	
	name = CharSequence::create( start_of_name, end_of_name );
	pch = p+1;
	return true;
}










} // namespace xml
 
} // namespace lix